package com.nw.dsl4j.generator;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;

import com.nw.dsl4j.annotations.Cardinality;
import com.nw.dsl4j.annotations.CardinalityType;
import com.nw.dsl4j.annotations.Parent;
import com.nw.dsl4j.annotations.Rule;
import com.nw.dsl4j.annotations.SubtypeAlternatives;
import com.nw.dsl4j.annotations.Token;

public class DsljReflectionUtils {
	public static CardinalityType getCardinality(Field f) {
		CardinalityType cardinalityType = CardinalityType.One;
		if (f.isAnnotationPresent(Cardinality.class)) {
			cardinalityType = f.getAnnotation(Cardinality.class).cardinalityType();
		} else {
			if (RefllectionUtils.isCollection(f)) {
				cardinalityType = CardinalityType.ZeroOrMore;
			}
		}
		return cardinalityType;
	}
	
	public static boolean classHasParentField(Class<?> type) {
		return getClassParentField(type) != null;
	}
	public static Field getClassParentField(Class<?> c) {
		// TODO: verify there isn't more than one Parent field
		// TODO: consider inheritance
		List<Field> allFields = getAllRuleRelatedFields(c);
		for (Field f : allFields) {
			if (f.isAnnotationPresent(Parent.class)) {
				return f;
			}
		}
		return null;
	}
	
	@SuppressWarnings("unchecked")
	public static List<Field> getAllRuleRelatedFields(Class cls) {
		Class tmp = cls;
		List<Field> fields = new ArrayList<Field>();
		while (tmp != null) {
			for (Field f : tmp.getDeclaredFields()) {
				fields.add(f);
			}

			Class superclass = tmp.getSuperclass();
			if (superclass == null) {
				tmp = null;
			} else {
				if (superclass.isAnnotationPresent(Rule.class)) {
					tmp = superclass;
				} else {
					tmp = null;
				}
			}
		}
		return fields;
	}

	@SuppressWarnings("unchecked")
	public static boolean isRule(Class cls) {
		return cls.isAnnotationPresent(Rule.class);
	}

	@SuppressWarnings("unchecked")
	public static boolean isToken(Class cls) {
		return cls.isAnnotationPresent(Token.class);
	}
	
	@SuppressWarnings("unchecked")
	public static boolean isSubTypeAlternative(Class cls) {
		return cls.isAnnotationPresent(SubtypeAlternatives.class);
	}

	@SuppressWarnings("unchecked")
	public static String getAntlrRuleName(Class cls) {
		String name = cls.getCanonicalName().replace(".", "_");
		if (cls.isAnnotationPresent(Rule.class)) {
			return StringUtils.uncapitalize(name);
		} else {
			return name.toUpperCase();
		}
	}
	public static Object getParent(Object q) {
		Class<?> qc = q.getClass();
		Field parentField = DsljReflectionUtils.getClassParentField(qc);
		try {
			if (parentField == null) {
				return null;
			} else {
				return PropertyUtils.getProperty(q, parentField.getName());
			}
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}
	
	public static Object findRootParent(Object o) {
		Object tmp = o;
		Object ret = o;
		while ((tmp = getParent(tmp)) != null) {
			ret = tmp;
		}
		return ret;
	}
}
